
#include "CalculatorCore.h"

using namespace std;
using namespace ttg_internal;
using namespace ttg;
using namespace ttg::extras;
using namespace ttg::strings;
using namespace ttg::math;

//--------------------------
//--- CalculatorRoutines ---
//--------------------------

int ttg_internal::CalculatorRoutines::getPriority(OperationInfo::OperationName ops)
{
	switch (ops)
	{
		case OperationInfo::ADDITION:
		case OperationInfo::SUBSTRACTION:
			return 1; break;

		case OperationInfo::MULTIPLICATION:
		case OperationInfo::DIVISION:
			return 2; break;

		case OperationInfo::UNARY_PLUS:
		case OperationInfo::UNARY_MINUS:
			return 3; break;

		default:
			return -1; break;
	}
}

ttg_internal::OperationInfo *CalculatorRoutines::buildFunction(string &name,
															   StringSplitter &sp,
															   int &n,
															   Params *params)
{
	if (name == "DRAND" || name == "VRAND")
	{
		string val = sp[n++];
		if (val[0] == '(')
		{
			//Getting args.
			OperationInfo *inf1 = build(sp, n, params);
			val = sp[n++];
			if (val != ",") //It's error!
			{
				SAFE_DELETE(inf1);
				return NULL;
			}
			OperationInfo *inf2 = build(sp, n, params);
			val = sp[n++];
			if (val != ")") //It's error!
			{
				SAFE_DELETE(inf1);
				SAFE_DELETE(inf2);
				return NULL;
			}
			//Checking.
			if (inf1->getType() == OperationInfo::TTGFLOAT)
			{
				Operation<float> *lower = dynamic_cast<Operation<float> *>(inf1);
				Operation<float> *upper = dynamic_cast<Operation<float> *>(inf2);
				if (upper == NULL)
				{
					SAFE_DELETE(inf1);
					SAFE_DELETE(inf2);
					return NULL;
				}
				if (name == "VRAND")
				{
					OperationVector *v1 = new OperationVector(lower, lower, lower);
					OperationVector *v2 = new OperationVector(upper, upper, upper);
					return new VRandOperation(v1, v2);
				}
				else
					return new DRandOperation(lower, upper);
			} else
			if (inf1->getType() == OperationInfo::TTGVECTOR3)
			{
				Operation<vec3f> *lower = dynamic_cast<Operation<vec3f> *>(inf1);
				Operation<vec3f> *upper = dynamic_cast<Operation<vec3f> *>(inf2);
				if (upper == NULL || name != "VRAND")
				{
					SAFE_DELETE(inf1);
					SAFE_DELETE(inf2);
					return NULL;
				}
				return new VRandOperation(lower, upper);
			}
			else
			{
				SAFE_DELETE(inf1);
				SAFE_DELETE(inf2);
				return NULL;
			}
		}
		else
			return NULL;
	}
	else
		return NULL;
}

ttg_internal::OperationInfo *CalculatorRoutines::buildWrapper(StringSplitter &sp, int &n, Params *params)
{
	const float *f;
	const vec3f *v;
	const mat4x4f *m;
	OperationInfo *func;
	float cf;
	string val = sp[n++];
	//Is it brackets?
	if (val[0] == '(')
	{
		OperationInfo *inf = build(sp, n, params);
		val = sp[n++];
		if (val == ")") //It's brackets!
			return inf; else
		if (val == ",") //It's a vector!
		{
			//Reading second component.
			OperationInfo *inf2 = CalculatorRoutines::build(sp, n, params);
			val = sp[n++];
			if (val != ",")
			{
				SAFE_DELETE(inf);
				SAFE_DELETE(inf2);
				return NULL;
			}
			//Reading third component.
			OperationInfo *inf3 = CalculatorRoutines::build(sp, n, params);
			val = sp[n++];
			if (val != ")" || inf == NULL || inf2 == NULL || inf3 == NULL ||
				inf->getType() != OperationInfo::TTGFLOAT ||
				inf2->getType() != OperationInfo::TTGFLOAT ||
				inf3->getType() != OperationInfo::TTGFLOAT)
			{
				SAFE_DELETE(inf);
				SAFE_DELETE(inf2);
				SAFE_DELETE(inf3);
				return NULL;
			}
			return new OperationVector(inf, inf2, inf3);
		}
		else //It's an error!
		{
			SAFE_DELETE(inf);
			return NULL;
		}
	} else
	//Is it float/int constant?
	if (isFloat(val, cf))
	{
		return new OperationValueWrapper<float>(cf);
	} else
	//Is it float variable?
	if ((f = params->getFloats().getConstRef(val.c_str())) != NULL)
	{
		return new OperationWrapper<float>(f);
	} else
	//Is it vector variable?
	if ((v = params->getVectors().getConstRef(val.c_str())) != NULL)
	{
		return new OperationWrapper<vec3f>(v);
	}else
	//Is it matrix variable?
	if ((m = params->getMatrices().getConstRef(val.c_str())) != NULL)
	{
		return new OperationWrapper<mat4x4f>(m);
	} else
	//Is it function?
	if ((func = buildFunction(val, sp, n, params)) != NULL)
		return func;
	//It is an error!
	else
		return NULL;
}

bool CalculatorRoutines::parse(StringSplitter &sp, int &n, Params *params,
							   list<ttg_internal::OperationInfo *> &vals,
							   list<ttg_internal::OperationInfo::OperationName> &ops)
{
	//Reading all operands
	OperationInfo *inf;				//Auxiliary
	string val;						//Auxiliary
	State cur = OPERAND_EXPECTED;
	while (n < (int)sp.getCount())
	{
		val = string(sp[n++]);
		switch (cur)
		{
			case OPERAND_EXPECTED:
				//is it unary + or -?
				if (val == "+" || val == "-")
				{
					ops.push_back(val == "+" ? OperationInfo::UNARY_PLUS
											 : OperationInfo::UNARY_MINUS);
					break;
				}
				//reading operand
				n--;
				if ((inf = buildWrapper(sp, n, params)) == NULL)
				{
					SAFE_DELETE_CONTENT(&vals);
					return false;
				}
				vals.push_back(inf);
				cur = OPERATION_EXPECTED;
				break;

			case OPERATION_EXPECTED:
				if (val == "+")
					ops.push_back(OperationInfo::ADDITION); else
				if (val == "-")
					ops.push_back(OperationInfo::SUBSTRACTION); else
				if (val == "*")
					ops.push_back(OperationInfo::MULTIPLICATION); else
				if (val == "/")
					ops.push_back(OperationInfo::DIVISION);
				else
				{
					n--;
					if (vals.empty()) //it's bracket ot comma.
					{
						SAFE_DELETE_CONTENT(&vals);
						return false;
					}
					else
						return true;
				}
				cur = OPERAND_EXPECTED;
				break;
		}
	}
	if (cur == OPERAND_EXPECTED || vals.empty())
	{
		SAFE_DELETE_CONTENT(&vals);
		return false;
	}
	return true;
}

ttg_internal::OperationInfo *CalculatorRoutines::buildNext(list<ttg_internal::OperationInfo *> &vals,
														   list<ttg_internal::OperationInfo::OperationName> &ops)
{
	if (!ops.empty())
	{
		//Getting operation
		OperationInfo::OperationName op = *ops.begin();
		ops.pop_front();
		//Analyzing
		while (!ops.empty())
		{
			OperationInfo::OperationName next = *ops.begin();
			if (getPriority(op) < getPriority(next))
			{
				OperationInfo *inf = *vals.begin();
				vals.pop_front();
				vals.push_front(buildNext(vals, ops));
				vals.push_front(inf);
			}
			else
				break;
		}
		//Building result
		if (op == OperationInfo::UNARY_MINUS ||
			op == OperationInfo::UNARY_PLUS)
		{
			OperationInfo *s = *vals.begin();
			vals.pop_front();
			return OperationMapper::create(op, s);
		}
		else
		{
			OperationInfo *first = *vals.begin();
			vals.pop_front();
			OperationInfo *second = *vals.begin();
			vals.pop_front();
			return OperationMapper::create(op, first, second);
		}
	}
	throw Error(Error::FATAL, "Calculator", "buildNext", "Internal error");
}

ttg_internal::OperationInfo *CalculatorRoutines::build(StringSplitter &sp, int &n, Params *params)
{
	list<OperationInfo *> vals;
	list<OperationInfo::OperationName> ops;
	if (!parse(sp, n, params, vals, ops))
		return NULL;
	while (ops.size() != 0)
		vals.push_front(buildNext(vals, ops));
	return *vals.begin();
}
